﻿using pdfer.PdfObjecs;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace pdfer.Syntax
{
    internal class CMapCompilationEngine
    {
        public void ParseToCMapObject(Byte[] data)
        {
            using var ms = new MemoryStream(data);
            ParseToCMapObject(ms);
        }

        public void ParseToCMapObject(Stream stream)
        {
            InitContext(stream);
            // 拆解 按空格或者换行符分割
            GetTokens();
            // 获取Token
            MakeCMapObj();
        }
        private CMapObject cmap;
        public CMapObject CMapObj
        {
            get { return cmap; }
        }

        private void MakeCMapObj()
        {
            cmap = new CMapObject();
            Boolean incmap = false;
            Int32 cmptype = 0;
            for(var i = 0; i < tokens.Count; i++)
            {
                var token = tokens[i];  
                if (incmap)
                {
                    switch (cmptype)
                    {
                        case 0:
                            {
                                if(token.Type == TokenType2.Keyword && token.Token == "endcodespacerange") 
                                {
                                    incmap = false;
                                }
                                else
                                {
                                    // 成对的 <> <>
                                    var next = tokens[i+1];
                                    i++;
                                    var count = next.Token.Length/2 - 1;
                                    cmap.CodeSpaceRange.Add(
                                        (Convert.ToInt32(token.Token[1..^1], 16)
                                        , Convert.ToInt32(next.Token[1..^1], 16),
                                        count)
                                        );
                                }
                            }
                            break;
                        case 1:
                            {
                                if (token.Type == TokenType2.Keyword && token.Token == "endbfchar")
                                {
                                    incmap = false;
                                }
                                else
                                {
                                    // 成对的 <> <>
                                    var next = tokens[i + 1];
                                    i++;
                                    cmap.BFChar.Add(Convert.ToInt32(token.Token[1..^1], 16), GetBytes(next.Token[1..^1]));
                                }
                            }
                            break;
                        case 2:
                            {
                                if (token.Type == TokenType2.Keyword && token.Token == "endbfrange")
                                {
                                    incmap = false;
                                }
                                else
                                {
                                    // <> <> <>/[]
                                    var next = tokens[i + 1];
                                    i++;
                                    var next2 = tokens[i + 1]; 
                                    i++;
                                    if(next2.Type == TokenType2.Symbol && next2.Token == "[")
                                    {
                                        var arr = InnerParseArr(ref i);
                                        cmap.BFRange2.Add((Convert.ToInt32(token.Token[1..^1], 16), Convert.ToInt32(next.Token[1..^1], 16), arr.ToArray()));
                                    }
                                    else
                                    {
                                        cmap.BFRange.Add((Convert.ToInt32(token.Token[1..^1], 16), Convert.ToInt32(next.Token[1..^1], 16), Convert.ToInt32(next2.Token[1..^1], 16)));
                                    }
                                }
                            }
                            break;
                    }
                }
                else
                {
                    // 等待 begincodespacerange beginbfchar beginbfrange
                    if(token.Type == TokenType2.Keyword)
                    {
                        if (token.Token == "begincodespacerange") 
                        {
                            incmap = true;
                            cmptype = 0;
                        }
                        else if (token.Token == "beginbfchar") 
                        {
                            incmap = true;
                            cmptype = 1;

                        }
                        else if (token.Token == "beginbfrange")
                        {
                            incmap = true;
                            cmptype = 2;

                        }
                    }
                }
            }
        }

        private static Byte[] GetBytes(String v)
        {
            var data = new Byte[v.Length / 2];
            var j = 0;
            for (var i = data.Length - 1; i >= 0; i--, j++,j++)
            {
                data [i] = Convert.ToByte(v[j..(j+2)],16);
            }
            return data;
        }

        private readonly static Regex RegWD = new(@"\w");
        private readonly static List<String> symbolTable1 = ["<<", "[", ">>", "]"];
        private readonly static List<String> keywordTable = ["findresource", "begin", "dict", "begincmap", "def",
            "begincodespacerange", "endcodespacerange", "true", "false", "beginbfchar", "endbfchar", "beginbfrange","endbfrange", "endcmap", "CMapName", 
            "currentdict", "defineresource", "pop", "end"];
        /// <summary>
        /// 运算符仅仅在流中才有效
        /// 绘图操作 设备无关的
        /// 文本操作
        /// 设备依赖的操作符号
        /// </summary>
        private static List<String> opearTable = [];

        #region context
        private Stream _stream;
        private Byte[] _buff;
        private Boolean inComments = false;
        private List<PSTokenV2> tokens;
        private List<Object> objs;
        private List<Byte> token;
        private Int32 leftDic;
        private Int32 leftArr;

        private Int32 _idx;
        private Int32 _ptr;
        private Int32 _buffDataLen;
        #endregion

        private void InitContext(Stream stream)
        {
            tokens = [];
            token = [];
            _stream = stream;
            _stream.Position = 0;
            _buff = new Byte[1024];
            inComments = false;
            leftDic = 0;
            leftArr = 0;
        }

        private void GetTokens()
        {
            while (true)
            {
                var (wd, hasVal) = Next();
                if (!hasVal)
                {
                    break;
                }
                if (inComments)
                {
                    if (wd == 0x0D || wd == 0x0A)
                    {
                        inComments = false;
                    }
                    continue;
                }
                if (RegWD.IsMatch(((Char)wd).ToString())
                    || wd == '.' // 数字
                    || wd == '-' // 数字符号 命名里面带有
                    || wd == '_'
                    || wd == '#' // CJK（中文、日文和韩文） 字体程序名称的多字节编码需要用#标识
                    || wd == '+' // 字体子集
                                 )
                {
                    token.Add(wd);
                }
                else if (wd == '/')
                {
                    if (token.Count > 0)
                    {
                        NewToken(_idx, [.. token]);
                        token = [];
                    }
                    token.Add(wd);
                }
                else if (wd == '\\')
                {
                    // 转义的情况
                    (wd, hasVal) = Next();
                    if (!hasVal)
                    {
                        break;
                    }
                    token.Add(wd);
                }
                else if (wd == '<')
                {
                    if (token.Count > 0)
                    {
                        NewToken(_idx, [.. token]);
                        token = [];
                    }
                    (wd, hasVal) = Next();
                    if (!hasVal)
                    {
                        break;
                    }
                    if (wd == '<')
                    {
                        token.Add((Byte)'<');
                        token.Add((Byte)'<');
                        NewToken(_idx, [.. token]);
                        if (End())
                        {
                            break;
                        }
                        token = [];
                    }
                    else
                    {
                        token.Add((Byte)'<');
                        token.Add(wd);
                        while (true)
                        {
                            // HexString
                            (wd, hasVal) = Next();
                            if (!hasVal)
                            {
                                // 字符串截断 报错
                                throw new Exception("未结束的字符串");
                            }
                            token.Add(wd);
                            if (wd == '>')
                            {
                                break;
                            }
                        }
                        NewToken(_idx, [.. token]);
                        if (End())
                        {
                            break;
                        }
                        token = [];
                    }
                }
                else if (wd == '>')
                {
                    (wd, hasVal) = Next();
                    if (wd == '>')
                    {
                        if (token.Count > 0)
                        {
                            NewToken(_idx, token.ToArray());
                            token = new();
                        }
                        token.Add((Byte)'>');
                        token.Add((Byte)'>');
                        NewToken(_idx, [.. token]);
                        if (End())
                        {
                            break;
                        }
                        token = [];
                    }
                    else
                    {
                        throw new Exception("期待>> 实际 >");
                    }
                }
                else if (wd == '(')
                {
                    if (token.Count > 0)
                    {
                        NewToken(_idx, token.ToArray());
                        if (End())
                        {
                            break;
                        }
                        token = [];
                    }
                    token.Add(wd);
                    var leftP = 1;
                    while (true)
                    {
                        (wd, hasVal) = Next();
                        if (!hasVal)
                        {
                            // 字符串截断 报错
                            throw new Exception("未结束的字符串");
                        }
                        token.Add(wd);
                        if (wd == '(')
                        {
                            if (token.Count < 3 || token[^2] != '\\')
                            {
                                leftP++;
                            }
                        }
                        else if (wd == ')')
                        {
                            if (token.Count < 3 || token[^2] != '\\')
                            {
                                leftP--;
                            }
                            if (leftP < 1)
                            {
                                break;
                            }
                        }
                    }
                    NewToken(_idx, [.. token]);
                    if (End())
                    {
                        break;
                    }
                    token = [];
                }
                else if (wd == '\r' || wd == '\n' || wd == ' ')
                {
                    if (token.Count > 0)
                    {
                        NewToken(_idx, [.. token]);
                        if (End())
                        {
                            break;
                        }
                        token = [];
                    }
                }
                else if (wd == '[' || wd == ']')
                {
                    if (token.Count > 0)
                    {
                        NewToken(_idx, [.. token]);
                        if (End())
                        {
                            break;
                        }
                        token = [];
                    }
                    token.Add(wd);
                    NewToken(_idx, [.. token]);
                    token = [];
                }
                else if (wd == '%')
                {
                    if (token.Count > 0)
                    {
                        NewToken(_idx, [.. token]);
                        token = [];
                    }
                    inComments = true;
                }
            }
        }

        private bool End()
        {
            var lastToken = tokens[^1];
            if(lastToken.Token == "endcmap")
            {
                // 暂时不看后面的toen了
                return true;
            }
            return false;
        }

        private (Byte, Boolean) Next()
        {
            if (_ptr == _buffDataLen)
            {
                _buffDataLen = _stream.Read(_buff);
                _ptr = 0;
                if (_buffDataLen == 0)
                {
                    return (0, false);
                }
            }
            var wd = _buff[_ptr++];
            _idx++;
            return (wd, true);
        }
        private void NewToken(Int32 index, Byte[] list)
        {
            var result = new PSTokenV2
            {
                Index = index - list.Length,
                Length = list.Length,
            };
            if (list[0] == '(' && list[^1] == ')')
            {
                result.Type = TokenType2.String;
                // 流里面的字符串内容编码可能是在其他部分进行重新映射的所以直接保留原始的bin数据
                result.ByteToken = ParsePdfEncodingString(list);
                result.Token = $"({Encoding.UTF8.GetString(list[1..^1])})";
            }
            else if (list[0] == '<' && list[^1] == '>')
            {
                result.Type = TokenType2.HexString;
                // 流里面的字符串内容编码可能是在其他部分进行重新映射的所以直接保留原始的bin数据
                result.ByteToken = ParsePdfEncodingString(list);
                result.Token = $"<{Encoding.UTF8.GetString(list[1..^1])}>";
                //result.Token = $"<{ParsePdfEncodingHexString(list[1..^1])}>";
            }
            else
            {
                result.Token = Encoding.UTF8.GetString(list);
                result.Type = GetTokenType(result.Token);
            }
            tokens.Add(result);
        }

        private static String ParsePdfEncodingHexString(ReadOnlySpan<Byte> by)
        {
            var arr = ParsePdfEncodingString(by);
            var sb = new StringBuilder(by.Length * 2);
            foreach (var b in arr)
            {
                sb.Append(b.ToString("X2"));
            }
            return sb.ToString();
        }

        private static Byte[] ParsePdfEncodingString(ReadOnlySpan<Byte> by)
        {
            var by2 = new Byte[by.Length + 1];
            var curr = 0;
            for (var __idx = 0; __idx < by.Length; __idx++)
            {
                if (by[__idx] == (Byte)'\\')
                {
                    // 查看下一个
                    var next = by[__idx + 1];
                    __idx++;
                    switch (next)
                    {
                        case 98:  // 0x62 b \b
                            next = 8;
                            break;
                        case 102: // 0x66 f \f
                            next = 12;
                            break;
                        case 110: // 0x6e n \n
                            next = 10;
                            break;
                        case 114: // 0x72 r \r
                            next = 13;
                            break;
                        case 116: // 0x74 t \t
                            next = 9;
                            break;
                        case 40: // 0x28 (
                        case 41: // 0x29 )
                        case 92: // 0x5c \
                            // 不需要操作
                            break;
                        case 10:
                            continue;
                        case 13:
                            next = by[__idx + 1];
                            if (next == 10)
                            {
                                //移动_idx
                                // 忽略 \r
                                __idx++;
                            }
                            continue;
                        default:
                            {
                                if (!Char.IsDigit((Char)next))
                                {
                                    break;
                                }
                                var c = 0;
                                var _ = 0;
                                do
                                {
                                    next = by[__idx];
                                    if (next < 48 || next > 55)
                                    {
                                        break;
                                    }
                                    __idx++;
                                    _ = (_ << 3) + next - 48;
                                    c++;
                                }
                                while (c < 3);
                                // \125
                                if (c > 2)
                                {
                                    next = (Byte)(_ & 0xff);
                                }
                                break;
                            }
                    }
                    by2[curr++] = next;
                }
                else
                {
                    by2[curr++] = by[__idx];
                }
            }
            if (curr % 2 == 1)
            {
                curr++;
            }
            return by2[..curr];
        }

        private static TokenType2 GetTokenType(string token)
        {
            if (String.IsNullOrEmpty(token))
            {
                return TokenType2.None;
            }
            if (symbolTable1.Any(p => p == token.ToLower()))
            {
                return TokenType2.Symbol;
            }
            if (keywordTable.Any(p => p == token.ToLower()))
            {
                return TokenType2.Keyword;
            }
            //if (opearTable.Any(P => P == token))
            //{
            //    return TokenType2.Opera;
            //}
            if (token.StartsWith('<') && token.EndsWith('>'))
            {
                return TokenType2.HexString;
            }
            if (token.StartsWith('/'))
            {
                return TokenType2.Name;
            }
            if (token.IndexOf('.') > -1)
            {
                if (Double.TryParse(token, out _))
                {
                    return TokenType2.Number;
                }
            }
            if (Int32.TryParse(token, out _))
            {
                return TokenType2.Int;
            }

            throw new NotSupportedException($"未知的Token类型： {token}");
        }

        private List<Byte[]> InnerParseArr(ref Int32 i)
        {
            var result = new List<Byte[]>();
            for (; i < tokens.Count; i++)
            {
                var __token = tokens[i];
                if (__token.Token == "]")
                {
                    break;
                }
                else
                {
                    result.Add(GetBytes(__token.Token[1..^1]));
                }
            }
            return result;
        }

    }
}
